/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.frontendproject;

import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataCompilerContext;
import com.inspur.edp.lcm.metadata.spi.MetadataCompileAction;
import com.inspur.edp.web.common.customexception.WebCustomException;
import com.inspur.edp.web.common.entity.TerminalType;
import com.inspur.edp.web.common.environment.ExecuteEnvironment;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.utility.ListUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.formmetadata.resolver.ResolveFormMetadataList;
import com.inspur.edp.web.frontendproject.changedetect.ChangeDetectExecuteManager;
import com.inspur.edp.web.frontendproject.changedetect.ChangeDetectExecuteResult;
import com.inspur.edp.web.frontendproject.changedetect.ChangeDetectExecuteType;
import com.inspur.edp.web.frontendproject.changedetect.context.ChangeDetectContext;
import com.inspur.edp.web.frontendproject.entity.ChosenFormList;
import com.inspur.edp.web.frontendproject.generate.FrontendProjectGenerate;
import com.inspur.edp.web.frontendproject.metadata.FormMetadataManager;
import com.inspur.edp.web.frontendproject.resolver.FormMetadataResolver;
import com.inspur.edp.web.jitengine.JITEngineManager;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * 前端工程编译器
 * 通过元数据的回调事件进行调用
 *
 * @author guozhiqi
 */
@Slf4j
public class FrontendProjectCompiler implements MetadataCompileAction {
    /**
     * 工程编译扩展实现
     */
    @Override
    public void metadataCompile(MetadataCompilerContext context) {
        if (context == null || StringUtility.isNullOrEmpty(context.getProjectPath())) {
            log.error("the Project Path is Empty When Compiling a Project");
            throw new WebCustomException("the Project Path is Empty ");
        }
        WebLogger.Instance.info("begin compile frontend project", FrontendProjectCompiler.class.getName());
        generateAndCompileFrontendProject(context);

    }

    /**
     * 编译前端工程
     */
    private void generateAndCompileFrontendProject(MetadataCompilerContext context) {
        // 因为此处涉及到元数据的变更检测 因此不在乎此处进行route.json 文件的保存
        // 编译前端工程：面向PC设备
        if (FormMetadataManager.checkFormMetadataExists(context.getProjectPath(), TerminalType.PC, ChosenFormList.getNewInstance(), false, false)) {
            generateAndCompileFrontendProject(context.getProjectPath(), TerminalType.PC, false);
        }

        if (FormMetadataManager.checkFormMetadataExists(context.getProjectPath(), TerminalType.MOBILE, ChosenFormList.getNewInstance(), false, false)) {
            // 编译前端工程：面向Mobile设备
            generateAndCompileFrontendProject(context.getProjectPath(), TerminalType.MOBILE, false);
        }
    }

    /**
     * 前端工程的编译分为四步：预编译、将表单元数据(.frm)解析成 JSON、基于 JSON 生成前端代码（如Angular、Vue等）、将前端代码构建成 JS
     *
     * @param projectPath 待编译工程路径
     */
    private void generateAndCompileFrontendProject(String projectPath, TerminalType terminalType, boolean isJieXiForm) {

        // 执行前端工程代码生成 如果生成失败 那么不进行编译动作
        if (generateFrontendProject(projectPath, terminalType)) {
            return;
        }

        // 将前端代码构建成 JS
        buildFrontendProject(projectPath, terminalType);
    }

    /**
     * 生成前端工程代码
     *
     * @param projectPath
     * @param terminalType
     * @return
     */
    private boolean generateFrontendProject(String projectPath, TerminalType terminalType) {
        // 1. 预编译(编译前检测)
        List<GspMetadata> formMetadataInCurentProjectList = FrontendProjectUtility.getFormMetadataList(projectPath, terminalType);
        if (ListUtility.isEmpty(formMetadataInCurentProjectList)) {
            return true;
        }

        ChangeDetectContext changeDetectContext = new ChangeDetectContext();
        changeDetectContext.setProjectPath(projectPath);
        changeDetectContext.setTerminalType(terminalType);
        WebLogger.Instance.info("Web生成变更检测开始执行，对应工程路径为：" + projectPath);
        ChangeDetectExecuteResult compileExecuteResult = ChangeDetectExecuteManager.execute(ChangeDetectExecuteType.Generate, changeDetectContext);
        // 生成前编译检查
        if (!compileExecuteResult.isAllPass()) {
            WebLogger.Instance.info(compileExecuteResult.getUnPassReason());

            ResolveFormMetadataList formMetataList = FormMetadataManager.getFormMetadataList(projectPath, terminalType, ChosenFormList.getNewInstance());
            // 不存在表单，将其认定为非前端工程
            if (formMetataList.isEmpty()) {
                log.debug("Debug_FrontendProjectCompiler_CompileFrontendProject: Current project is not a frontend project or has no forms!");
                return true;
            }
            // 2. 解析表单元数据
            FormMetadataResolver.resolveFormMetadatas(projectPath, formMetataList, terminalType);

            // 3. 基于 JSON 生成前端代码
            WebLogger.Instance.info("开始执行Jit", FrontendProjectCompiler.class.getName());
            FrontendProjectGenerate.generateFrontendProject(projectPath, terminalType);

            // 生成完毕 执行元数据变更回写
            ChangeDetectExecuteManager.updateChangeset(ChangeDetectExecuteType.Generate, changeDetectContext);
        } else {
            WebLogger.Instance.info("Web生成变更检测，未发生变更，不执行前端代码生成。对应工程路径为：" + projectPath);
        }
        return false;
    }

    /**
     * 构建前端工程
     */
    public final void buildFrontendProject(String projectPath, boolean isJieXiForm) {
        if (FormMetadataManager.checkFormMetadataExists(projectPath, TerminalType.PC, ChosenFormList.getNewInstance(), isJieXiForm, true)) {
            buildFrontendProject(projectPath, TerminalType.PC);
        }
        if (FormMetadataManager.checkFormMetadataExists(projectPath, TerminalType.MOBILE, ChosenFormList.getNewInstance(), isJieXiForm, true)) {
            buildFrontendProject(projectPath, TerminalType.MOBILE);
        }
    }

    /**
     * 构建前端工程
     */
    public final void buildFrontendProject(String projectPath, TerminalType terminalType) {
        // 增加编译前变更检测
        WebLogger.Instance.info("开始执行编译前变更检测，对应工程路径为：" + projectPath + ",表单类型:" + terminalType.getFormName());

        ChangeDetectContext changeDetectContext = new ChangeDetectContext();
        changeDetectContext.setProjectPath(projectPath);
        changeDetectContext.setTerminalType(terminalType);
        ChangeDetectExecuteResult compileExecuteResult = ChangeDetectExecuteManager.execute(ChangeDetectExecuteType.Compile, changeDetectContext);
        if (compileExecuteResult.isAllPass()) {
            // 如果未检测完毕  那么不进行编译
            WebLogger.Instance.info("Web编译变更检测，未发生任何变更，不执行代码编译。对应工程路径为：" + projectPath + ",表单类型：" + terminalType.getFormName());
        } else {
            WebLogger.Instance.info(compileExecuteResult.getUnPassReason());

            JITEngineManager.buildFrontendProject(projectPath, ExecuteEnvironment.Design, terminalType);

            // 执行元数据变更回写
            ChangeDetectExecuteManager.updateChangeset(ChangeDetectExecuteType.Compile, changeDetectContext);
        }
    }

    /**
     * 使用Babel构建前端工程
     *
     * @param projectPath
     */
    public final void buildFrontendProjectForBabel(String projectPath) {
        JITEngineManager.buildFrontendProjectForBabel(projectPath);
    }
}
